package ru.housewife.controllers;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import ru.common.hibernate.HibernateActionExecutor;
import ru.housewife.AppConstants;
import ru.housewife.cache.CacheManager;
import ru.housewife.form.*;
import ru.housewife.model.EatingMenuDishRecipe;
import ru.housewife.model.EatingType;
import ru.housewife.service.dishes.*;
import ru.housewife.service.eatingmenus.AddNewEatingMenu;

import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyEditorSupport;
import java.util.*;

/**
 * controller for managing dish recipes
 *
 * @author Alexander Troshanin
 */
@Controller
public class DishRecipeController extends AuthorizedController implements AppConstants {

    private static final int COMMON_RECIPE_GROUPS   = 1;
    private static final int USER_RECIPE_GROUPS     = 2;
    private static final int USER_FAV_RECIPE_GROUPS = 3;

    private final HibernateActionExecutor hiber;

    public DishRecipeController(@Qualifier("houseWifesHibernate") HibernateActionExecutor hiber) {
        this.hiber = hiber;
    }

    @RequestMapping(value = "/users-recipes-list.html")
    public Object getUsersDishRecipes(HttpServletRequest request) {
        UserForm user = getUserForm(request);
        ModelAndView ret = new ModelAndView("housewifes/recipes/usersRecipesPage");
        ret.addObject("user", user);
        ret.addObject("recipes", user.getUserRecipes());
        return ret;
    }

    @RequestMapping(value = "/users-recipe-get-view.html")
    public Object getUsersDishRecipeForView(long id, HttpServletRequest request) {
        UserForm user = getUserForm(request);
        CookingDishRecipeForm recipe = user.getUsersCookingDishRecipeForm(id);
        if (recipe == null) {
            return "redirect:/users-recipes-list.html";
        }
        ModelAndView ret = new ModelAndView("housewifes/recipes/viewDishRecipePage");
        ret.addObject("user", user);
        ret.addObject("recipe", recipe);
        ret.addObject("preferableWaresForComponents", user.getPreferableWaresForComponents());
        ret.addObject("isUsersRecipe", true);
        return ret;
    }

    @RequestMapping(value = "/eatingmenu-recipe-get-view.html")
    public Object getDishRecipeOfEatingMenuForView(long usersTableId, long dayMenuId, long eatingMenuId, long id, HttpServletRequest request) {
        UserForm user = getUserForm(request);
        UsersTableForm usersTable = user.getUsersTable(usersTableId);
        if (usersTable == null) {
            return "redirect:/userstables.html";
        }
        DayMenuForm dayMenu = usersTable.getDayMenu(dayMenuId);
        if (dayMenu == null) {
            return "redirect:/daymenus-list.html?usersTableId=" + usersTableId;
        }
        EatingMenuForm eatingMenu = dayMenu.getEatingMenu(eatingMenuId);
        if (eatingMenu == null) {
            return "redirect:/eatingmenus-list.html?usersTableId=" + usersTableId + "&dayMenuId=" + dayMenuId;
        }
        EatingMenuDishRecipeForm eatingMenuRecipe = eatingMenu.getEatingMenuRecipe(id);
        if (eatingMenuRecipe == null) {
            return "redirect:/eatingmenu-recipes-list.html?usersTableId=" + usersTableId + "&dayMenuId=" + dayMenuId + "&eatingMenuId=" + eatingMenuId;
        }
        ModelAndView ret = new ModelAndView("housewifes/recipes/viewDishRecipePage");
        CookingDishRecipeForm recipeForm = eatingMenuRecipe.getRecipe();
        ret.addObject("user", user);
        ret.addObject("recipe", recipeForm);
        ret.addObject("usersWaresMap", user.getUsersWaresMap());
        ret.addObject("preferableWaresForComponents", user.getPreferableWaresForComponents());
        ret.addObject("isUsersRecipe", eatingMenuRecipe.isUserRecipeOwner());
        ret.addObject("usersTableId", usersTableId);
        ret.addObject("dayMenuId", dayMenuId);
        ret.addObject("eatingMenuId", eatingMenuId);
        return ret;
    }

    @RequestMapping(value = "/eatingmenu-recipes-list.html")
    public Object getDishRecipesForEatingMenu(long usersTableId, long dayMenuId, long eatingMenuId, HttpServletRequest request) {
        UserForm user = getUserForm(request);
        ModelAndView ret = new ModelAndView("housewifes/recipes/eatingMenuRecipesPage");
        ret.addObject("user", user);
        UsersTableForm userTable = user.getUsersTable(usersTableId);
        if (userTable == null) {
            return "redirect:/userstables.html";
        }
        DayMenuForm dayMenu = userTable.getDayMenu(dayMenuId);
        if (dayMenu == null) {
            return "redirect:/daymenus-list.html?usersTableId=" + usersTableId;
        }
        ret.addObject("userTable", userTable);
        ret.addObject("dayMenu", dayMenu);
        ret.addObject("eatingMenu", dayMenu.getEatingMenu(eatingMenuId));
        return ret;
    }

    @RequestMapping(value = "/eatingmenu-recipes-update-count.html", method = RequestMethod.GET)
    public Object updateEatingMenuRecipeCount(long usersTableId, long dayMenuId, long eatingMenuId, long eatingMenuRecipeId, int count, HttpServletRequest request) {
        ModelAndView ret = new ModelAndView("housewifes/eatingmenus/updateRecipesCountResult-json-reply");
        UserForm user = getUserForm(request);
        UsersTableForm usersTable = user.getUsersTable(usersTableId);
        if (usersTable == null) {
            ret.addObject("result", "FAIL");
            return ret;
        }
        DayMenuForm dayMenu = usersTable.getDayMenu(dayMenuId);
        if (dayMenu == null) {
            ret.addObject("result", "FAIL");
            return ret;
        }
        EatingMenuForm eatingMenu = dayMenu.getEatingMenu(eatingMenuId);
        if (eatingMenu == null) {
            ret.addObject("result", "FAIL");
            return ret;
        }
        EatingMenuDishRecipeForm eatingMenuRecipe = eatingMenu.getEatingMenuRecipe(eatingMenuRecipeId);
        if (eatingMenuRecipe == null) {
            ret.addObject("result", "FAIL");
            return ret;
        }
        eatingMenuRecipe.setCount(count);
        hiber.execute(new UpdateEatingMenuDishCount(eatingMenuRecipe));
        ret.addObject("result", "OK");
        return ret;
    }

    @RequestMapping(value = "/eatingmenu-recipes-add-existing.html", method = RequestMethod.GET)
    public Object addExistingDishRecipeToEatingMenu(long recipeId, long usersTableId, long dayMenuId, long eatingMenuId, HttpServletRequest request) {
        // TODO: finish implementation of this method!!!
        UserForm user = getUserForm(request);
        UsersTableForm usersTable = user.getUsersTable(usersTableId);
        if (usersTable == null) {
        }
        DayMenuForm dayMenu = usersTable.getDayMenu(dayMenuId);
        if (dayMenu == null) {
        }
        EatingMenuForm eatingMenu = dayMenu.getEatingMenu(eatingMenuId);
        if (eatingMenu == null) {
        }

        EatingMenuDishRecipeForm existingEatingMenuRecipe = eatingMenu.getEatingMenuRecipeByRecipeId(recipeId);
        ModelAndView ret = new ModelAndView("housewifes/eatingmenus/eatingMenuRecipeBlock-json-reply");
        if (existingEatingMenuRecipe == null) {
            CookingDishRecipeForm recipe = user.getUsersCookingDishRecipeForm(recipeId);
            recipe = (recipe == null) ? CacheManager.getInstance().getCookingDishRecipeForm(recipeId) : recipe;
            if (recipe == null) {
                // TODO: throw exception here or redirect...
            }
            EatingMenuDishRecipe eatingMenuRecipe = hiber.execute(new AddDishToEatingMenu(eatingMenu, user, recipe));
            if (eatingMenuRecipe == null) {
                // TODO: throw exception here or redirect...
            }
            ret.addObject("usersTableId", usersTableId);
            ret.addObject("dayMenuId", dayMenuId);
            ret.addObject("eatingMenuId", eatingMenuId);
            ret.addObject("eatingMenuRecipe", eatingMenu.getEatingMenuRecipe(eatingMenuRecipe.getId()));
        } else {
            existingEatingMenuRecipe.setCount(existingEatingMenuRecipe.getCount() + 1);
            hiber.execute(new UpdateEatingMenuDishCount(existingEatingMenuRecipe));
            ret.addObject("existingId", existingEatingMenuRecipe.getId());
            ret.addObject("count", existingEatingMenuRecipe.getCount());
        }
        return ret;
    }

    @RequestMapping(value = "/daymenu-add-existing-recipe.html", method = RequestMethod.POST)
    public Object addExistingDishRecipeToDayMenu(long recipeId, long usersTableId, long dayMenuId, String type, HttpServletRequest request) {

        EatingType eatingType = EatingType.valueOf(type);

        // TODO: finish implementation of this method!!!
        UserForm user = getUserForm(request);
        UsersTableForm usersTable = user.getUsersTable(usersTableId);
        if (usersTable == null) {
            // todo: throw exception or redirect
        }
        DayMenuForm dayMenu = usersTable.getDayMenu(dayMenuId);
        if (dayMenu == null) {
            // todo: throw exception or redirect
        }

        EatingMenuForm eatingMenu = dayMenu.getEatingMenuByType(eatingType);

        if (eatingMenu == null) {
            eatingMenu = new EatingMenuForm();
            eatingMenu.setDayMenu(dayMenu);
            eatingMenu.setEatingType(eatingType);
            hiber.execute(new AddNewEatingMenu(eatingMenu));
            eatingMenu = dayMenu.getEatingMenuByType(eatingType);
        }

        EatingMenuDishRecipeForm existingEatingMenuRecipe = eatingMenu.getEatingMenuRecipeByRecipeId(recipeId);
        if (existingEatingMenuRecipe == null) {
            CookingDishRecipeForm recipe = user.getUsersCookingDishRecipeForm(recipeId);
            recipe = (recipe == null) ? CacheManager.getInstance().getCookingDishRecipeForm(recipeId) : recipe;
            if (recipe == null) {
                // TODO: throw exception here or redirect...
            }
            EatingMenuDishRecipe eatingMenuRecipe = hiber.execute(new AddDishToEatingMenu(eatingMenu, user, recipe));
            if (eatingMenuRecipe == null) {
                // TODO: throw exception here or redirect...
            }
        } else {
            existingEatingMenuRecipe.setCount(existingEatingMenuRecipe.getCount() + 1);
            hiber.execute(new UpdateEatingMenuDishCount(existingEatingMenuRecipe));
        }
        ModelAndView ret = new ModelAndView("housewifes/daymenus/dayMenu-json-data");
        ret.addObject("dayMenuJson", new DayMenuWebWrapperForm(usersTable, dayMenu, dayMenu.getDayNumber(), false, Calendar.getInstance()));
        return ret;
    }

    @RequestMapping(value = "/eatingmenu-recipes-remove.html")
    public Object removeDishRecipeFromEatingMenu(long usersTableId, long dayMenuId, long eatingMenuId, long id, HttpServletRequest request) {
        UserForm user = getUserForm(request);
        UsersTableForm usersTable = user.getUsersTable(usersTableId);
        ModelAndView ret = new ModelAndView("housewifes/eatingmenus/removeRecipeResult-json-reply");
        if (usersTable == null) {
            return ret.addObject("result", "FAIL");
        }
        DayMenuForm dayMenu = usersTable.getDayMenu(dayMenuId);
        if (dayMenu == null) {
            return ret.addObject("result", "FAIL");
        }
        EatingMenuForm eatingMenu = dayMenu.getEatingMenu(eatingMenuId);
        if (eatingMenu == null) {
            return ret.addObject("result", "FAIL");
        }
        EatingMenuDishRecipeForm eatingMenuRecipe = eatingMenu.getEatingMenuRecipe(id);
        if (eatingMenuRecipe == null) {
            return ret.addObject("result", "FAIL");
        }
        hiber.execute(new RemoveDishFromEatingMenu(eatingMenu, eatingMenuRecipe));
        return ret.addObject("result", "OK");
    }

    @RequestMapping(value = "/users-recipes-remove.html")
    public Object removeUsersDishRecipe(long id, HttpServletRequest request) {
        UserForm user = getUserForm(request);
        CookingDishRecipeForm recipe = user.getUsersCookingDishRecipeForm(id);
        if (recipe == null) {
            return "redirect:/users-recipes-list.html";
        }
        // TODO: tbd
        //hiber.execute(new RemoveDishFromEatingMenu(eatingMenu, recipe));
        user.updateUsersRecipesGroups();
        return "redirect:/users-recipes-list.html";
    }

    @RequestMapping(value = "/get-select-recipes-form.html")
    public ModelAndView getSelectRecipeForm(HttpServletRequest request) {
        UserForm user = getUserForm(request);
        List<CookingDishRecipeGroupForm> commonRecipesGroups = CacheManager.getInstance().getFirstLevelRecipesGroups();
        SortedSet<UserForm.UsersRecipeGroup> userRecipesGroups = user.getFirstLevelUsersRecipesGroups();
        ModelAndView mv = new ModelAndView("housewifes/recipes/selectRecipeDialog-json-reply");
        mv.addObject("commonRecipesGroups", commonRecipesGroups);
        mv.addObject("userRecipesGroups", userRecipesGroups);

        mv.addObject("commonRecipeGroupType", COMMON_RECIPE_GROUPS);
        mv.addObject("userRecipeGroupType", USER_RECIPE_GROUPS);
        mv.addObject("userFavRecipeGroupType", USER_FAV_RECIPE_GROUPS);
        return mv;
    }

    @RequestMapping(value = "/get-select-recipe-groups-form.html")
    public ModelAndView getSelectRecipeGroupForm() {
        List<CookingDishRecipeGroupForm> recipesGroups = CacheManager.getInstance().getFirstLevelRecipesGroups();
        ModelAndView mv = new ModelAndView("housewifes/recipes/selectRecipeGroupDialog-json-reply");
        mv.addObject("recipesGroups", recipesGroups);
        return mv;
    }

    @RequestMapping(value = "/get-recipe-groups-json.html")
    public ModelAndView getRecipeGroupsJson(HttpServletRequest request) {
        UserForm user = getUserForm(request);
        ModelAndView mv = new ModelAndView("housewifes/recipes/recipeGroups-json-reply");
        mv.addObject("commonRecipeGroups", CacheManager.getInstance().getFirstLevelRecipesGroups());
        mv.addObject("userRecipeGroups", user.getFirstLevelUsersRecipesGroups());
        return mv;
    }

    @RequestMapping(value = "/get-common-recipes-json")
    public ModelAndView getCommonRecipesJson(HttpServletRequest request, long groupId, int rowLimit, int pageSize, int page) {
        CookingDishRecipeGroupForm group = groupId != 0 ?
                CacheManager.getInstance().getCookingDishRecipeGroupForm(groupId) :
                CacheManager.getInstance().getRootZeroGroup();
        List<CookingDishRecipeForm> recipes = group.getAllRecipes();

        return getRecipesJson(recipes, rowLimit, pageSize, page);
    }

    @RequestMapping(value = "/get-user-recipes-json")
    public ModelAndView getUserRecipesJson(HttpServletRequest request, long groupId, int rowLimit, int pageSize, int page) {
        UserForm user = getUserForm(request);
        UserForm.UsersRecipeGroup userRecipesGroup = groupId != 0 ? user.getUsersRecipesGroups().get(groupId) : user.getRootZeroGroup();

        List<CookingDishRecipeForm> recipes = userRecipesGroup != null ? userRecipesGroup.getAllRecipes() : Collections.<CookingDishRecipeForm>emptyList();

        return getRecipesJson(recipes, rowLimit, pageSize, page);
    }

    private ModelAndView getRecipesJson(List<CookingDishRecipeForm> recipes, int rowLimit, int pageSize, int page) {
        int totalPageCount = recipes.size() / pageSize;
        int from = page * pageSize;
        if (from > recipes.size()) {
            page = 0;
            from = 0;
        }
        int to = from + rowLimit;
        to = (to > recipes.size()) ? recipes.size() : to;

        List<PageInfo<CookingDishRecipeForm>> pages = new ArrayList<PageInfo<CookingDishRecipeForm>>();
        PageInfo<CookingDishRecipeForm> curPageInfo = new PageInfo<CookingDishRecipeForm>(page);
        pages.add(curPageInfo);
        int recipeIndex = 0;
        for (CookingDishRecipeForm recipe : recipes.subList(from, to)) {

            curPageInfo.addElement(recipe);

            recipeIndex++;
            if (recipeIndex == pageSize) {
                recipeIndex = 0;
                int newPageNum = curPageInfo.getPageNumber() + 1;
                curPageInfo = new PageInfo<CookingDishRecipeForm>(newPageNum);
                pages.add(curPageInfo);
            }
        }

        ModelAndView mv = new ModelAndView("housewifes/recipes/recipes-json-reply");
        mv.addObject("pages", pages);
        mv.addObject("totalPageCount", totalPageCount);
        return mv;
    }

    @RequestMapping(value = "/get-recipes-list.html")
    public ModelAndView getGetRecipes(long groupId,
                                      @RequestParam(required = true) int groupType,
                                      @RequestParam(required = false) int page,
                                      HttpServletRequest request) {

        switch (groupType) {
            case COMMON_RECIPE_GROUPS: {
                CookingDishRecipeGroupForm group = CacheManager.getInstance().getCookingDishRecipeGroupForm(groupId);
                List<CookingDishRecipeForm> recipesList;
                List<CookingDishRecipeGroupForm> childGroups;
                int pageCount;
                if (group != null) {
                    int origSize = group.getRecipes().size();
                    if (origSize > 0) {
                        int from = SELECT_RECIPES_VIEW_PAGE_SIZE * page;
                        from = (from >= origSize) ? 0 : from;
                        int to = from + SELECT_RECIPES_VIEW_PAGE_SIZE;
                        to = (to >= origSize) ? origSize : to;

                        recipesList = group.getRecipes().subList(from, to);
                    } else {
                        recipesList = Collections.emptyList();
                    }
                    pageCount = getPageCount(SELECT_RECIPES_VIEW_PAGE_SIZE, origSize);
                    childGroups = group.getChilds();
                } else {
                    recipesList = Collections.emptyList();
                    childGroups = Collections.emptyList();
                    pageCount = 0;
                    page = 0;
                }
                ModelAndView mv = new ModelAndView("housewifes/recipes/recipesList-json-reply");
                mv.addObject("group", group);
                mv.addObject("recipes", recipesList);
                mv.addObject("childGroups", childGroups);
                mv.addObject("selectedPage", page);
                mv.addObject("pageCount", pageCount);
                mv.addObject("pageNums", generatePageNumsInfo(page, pageCount));
                return mv;
            }
            case USER_RECIPE_GROUPS:{
                UserForm user = getUserForm(request);
                UserForm.UsersRecipeGroup userRecipesGroup = user.getUsersRecipesGroups().get(groupId);

                List<CookingDishRecipeForm> recipesList;
                List<UserForm.UsersRecipeGroup> childGroups;
                int pageCount;
                if (userRecipesGroup != null) {
                    int origSize = userRecipesGroup.getRecipes().size();
                    if (origSize > 0) {
                        int from = SELECT_RECIPES_VIEW_PAGE_SIZE * page;
                        from = (from >= origSize) ? 0 : from;
                        int to = from + SELECT_RECIPES_VIEW_PAGE_SIZE;
                        to = (to >= origSize) ? origSize : to;

                        recipesList = userRecipesGroup.getRecipes().subList(from, to);
                    } else {
                        recipesList = Collections.emptyList();
                    }
                    pageCount = getPageCount(SELECT_RECIPES_VIEW_PAGE_SIZE, origSize);
                    childGroups = userRecipesGroup.getChilds();
                } else {
                    recipesList = Collections.emptyList();
                    childGroups = Collections.emptyList();
                    pageCount = 0;
                    page = 0;
                }
                ModelAndView mv = new ModelAndView("housewifes/recipes/recipesList-json-reply");
                mv.addObject("group", userRecipesGroup);
                mv.addObject("recipes", recipesList);
                mv.addObject("childGroups", childGroups);
                mv.addObject("selectedPage", page);
                mv.addObject("pageCount", pageCount);
                mv.addObject("pageNums", generatePageNumsInfo(page, pageCount));
                return mv;
            }
            case USER_FAV_RECIPE_GROUPS: {
                return null;
            }
            default: {
                return null;
            }

        }

    }

    @InitBinder
    public void initBinder(WebDataBinder binder) {

        binder.registerCustomEditor(EatingType.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(final String text) throws IllegalArgumentException {
                setValue(EatingType.valueOf(text));
            }
        });
    }
}
